import os
import asyncio
import time
import datetime
from threading import Thread

from common.third_party.save_config import save_const, load_config
from services.server_integration.schemas.ad_ul_dl import AdUlDl
from settings import settings
import subprocess
from services.ad936x.api import Ad936xApi
from common.base.base_handler import BaseHandler
from services.new_services.schemas.classes import BlockType
from services.server_integration.schemas.alarms import Alarm
from services.new_services.schemas.cviya_config import BLOCKS
from services.server_integration.model import ServerIntegration
from services.server_integration.schemas.alarms_request import AlarmsRequest
from services.server_integration.schemas.alarms import OldestNewestAlarmsDate
from services.server_integration.schemas.technical_status import TechnicalStatus
from services.server_integration.schemas.calibrate_status import CalibrateMessage
from services.server_integration.schemas.full_unit_configuration import FullUnitConfiguration
from services.server_integration.schemas.alarms_response_configuration import AlarmResponseConfiguration
from services.server_integration.schemas.cities_timezone import TimezonesRequest, Timezones, CityByTimezoneRequest
from services.new_services.new_server_integration.schemas.zcalibration import ZCalibrationTelemetry, ZCalibrationInfo,\
    ZCalibrationStatus, ZCalibrationDevice

def _device_to_index(device):
    return int(device[2:]) + (2 if 'SM' in device or 'BS' in device else 0)


class ServerIntegrationHandler(BaseHandler):
    __slots__ = ['model']

    async def handle_set_full_configuration(self, params: dict) -> bool:
        prev = self.model.full_unit_config

        uuid, version_info=\
            self.model.full_unit_config.unit_configuration.uuid,\
            self.model.full_unit_config.unit_configuration.version_info

        self.model.telemetry_lock = datetime.datetime.now()
        config, user_role = params['config'], params['user_role']
        self.model.full_unit_config = self.model.down_convert_values(
            self.model.set_indexes(
                self.model.full_unit_config.copy(deep=True)
            )
        )
        config = {k if v is not None else '': v for k, v in config.items()}
        full_unit_config = FullUnitConfiguration(**{**self.model.full_unit_config.dict(), **config})
        self.model.full_unit_config = self.model.up_convert_values(
            self.model.set_indexes(
                full_unit_config.copy(deep=True)
            )
        )

        self.model.full_unit_config.unit_configuration.uuid = uuid
        self.model.full_unit_config.unit_configuration.version_info = version_info

        await self.model.process_full_unit_config(self.model.full_unit_config, False, prev)
        ServerIntegration.save_full_config(self.model.full_unit_config, user_role == 'adjuster')

        return True

    async def handle_get_full_configuration(self, params: dict) -> FullUnitConfiguration:
        # self.model.update_ip_address()
        result = self.model.down_convert_values(
            self.model.set_indexes(
                self.model.full_unit_config.copy(deep=True)
            )
        )

        return result

    async def handle_start_calibrate(self) -> bool:
        return await self.model.start_calibrate()

    async def handle_special_calibrate(self, params):
        device, power = params['device'], params['power']
        index = _device_to_index(device)

        return await self.model.special_calibrate(index, power)

    async def handle_zcalibration_calibrate(self, params) -> ZCalibrationStatus:
        device, target_power = params['device'], params['target_power']

        return await self.model.zcalibration_calibrate(device, target_power)

    async def handle_zcalibration_telemetry(self, params) -> ZCalibrationTelemetry:
        device = params['device']

        return await self.model.zcalibration_telemetry(device)

    async def handle_zcalibration_info(self, params) -> ZCalibrationInfo:
        device = params['device']

        return await self.model.zcalibration_info(device)

    async def handle_zcalibration_save(self, params):
        device, info = params['device'], ZCalibrationInfo(**params['info'])

        await self.model.zcalibration_save(device, info)
        return True

    async def handle_set_ac_magic_numbers(self, params):
        device, correct_number, real_power, power =\
            params['device'], params['correct_number'], params['real_power'], params['power']

        index = _device_to_index(device)
        await self.model.set_ac_magic_numbers(index, correct_number, real_power, power)

        return True

    async def handle_get_ac_magic_numbers(self, params: dict):
        device: str = params['device']
        index = _device_to_index(device)

        if self.model.full_unit_config.unit_configuration.unit_type == 'FSC':
            device = device.replace('SM', "BS")

        magic_numbers = await self.model.get_ac_magic_numbers(index)
        magic_numbers.device_name = device
        return magic_numbers

    async def handle_set_rtr_normal_vswr(self, params):
        index, vswr = params['index'], params['vswr']
        self.model.set_rtr_normal_vswr(index, vswr)

        return True

    async def handle_get_rtr_normal_vswr(self, params) -> float:
        index = params['index']
        return self.model.get_rtr_normal_vswr(index)

    async def handle_calibrate_rtr_vswr(self, params) -> float:
        index = params['index']
        return await self.model.calibrate_rtr_vswr(index)

    async def handle_get_technical_status(self) -> TechnicalStatus:
        technical_status = self.model.technical_status.copy(deep=True)

        # if user_role == 'adjuster':
        #     technical_status.gps_telemetry.last_capture_time.is_need = True

        return technical_status

    async def handle_get_newest_oldest_alarms_date(self, params: dict) -> OldestNewestAlarmsDate:
        user_role = params['user_role']
        result = self.model.get_newest_oldest_alarms_date(user_role)
        return OldestNewestAlarmsDate(
            newest_date=datetime.datetime.utcfromtimestamp(1),
            oldest_date=datetime.datetime(2038, 1, 13, 13, 13, 13, 13),
            alarms_total_count=result.alarms_total_count
        )

    async def handle_get_current_alarms(self, params: dict) -> AlarmResponseConfiguration:
        request, user_role = AlarmsRequest(**params['request']), params['user_role']
        result: AlarmResponseConfiguration = self.model.get_current_alarms_filtered(request, user_role)
        return result

    async def handle_get_alarms_log(self, params: dict) -> AlarmResponseConfiguration:
        request, user_role = AlarmsRequest(**params['request']), params['user_role']
        result: AlarmResponseConfiguration = self.model.get_alarms_log_filtered(request, user_role)
        return result

    async def handle_raise_info(self, params: dict) -> bool:
        name, value, end_value = params['name'], params['value'], params['end_value']
        self.model.raise_info(name, value, end_value)
        return True

    async def handle_raise_alarm(self, params: dict) -> bool:
        name, state, value = params['name'], params['state'], params['value']
        self.model.raise_alarm(name, state, value)
        return True

    async def handle_load_alarms_descriptions(self):
        return self.model.load_alarms_description()

    async def handle_get_cities_timezones(self, params: dict) -> Timezones:
        request = TimezonesRequest(**params['request'])
        return self.model.get_cities_timezones(request)

    async def handle_get_city_by_timezone(self, params: dict) -> CityByTimezoneRequest:
        request = CityByTimezoneRequest(**params['request'])
        return self.model.get_city_by_timezone(request)

    async def handle_reboot_motherboard(self):
        self.model.reboot_motherboard()

    async def handle_reboot_power(self):
        await self.model.reboot_power()
        
    async def handle_get_server_ip(self):
        return self.model.full_unit_config.unit_configuration.server.ip

    async def handle_get_server_proxy_ip(self):
        return self.model.full_unit_config.unit_configuration.server.proxy_ip

    async def handle_get_serial_number(self):
        return self.model.full_unit_config.unit_configuration.main_module.serial_number

    async def handle_clear_alarms(self):
        self.model.clear_alarms()
        return True

    async def handle_set_calibrate_status(self, params: dict):
        msg = CalibrateMessage(**params['msg'])
        await self.model.set_calibrate_status(msg)
        return True

    async def handle_get_cviya_list(self) -> list[str]:
        if self.model.full_unit_config.unit_configuration.unit_type == BlockType.FSR:
            return [block.cviya for block in BLOCKS if block.block_type == BlockType.FSR]
        else:
            return [block.cviya for block in BLOCKS if block.block_type == BlockType.FSC]

    async def handle_get_timezone(self):
        return self.model.full_unit_config.unit_configuration.timezone

    async def handle_set_production_state(self, params: dict):
        state = params['state']
        with open(f'{settings.APP_PATH}/settings.py', 'tr+') as file:
            data = file.read()
            file.seek(0)
            file.truncate()
            if state:
                new_data = data.replace('PRODUCTION: bool = False', 'PRODUCTION: bool = True')
            else:
                new_data = data.replace('PRODUCTION: bool = True', 'PRODUCTION: bool = False')

            file.write(new_data)

        def reload_main():
            time.sleep(1.0)
            os.system('systemctl stop rcs-main')
            os.system('systemctl start rcs-main')

        Thread(target=reload_main).start()

        return True

    async def handle_get_production_state(self):
        with open(f'{settings.APP_PATH}/settings.py', 'tr') as file:
            data = file.read()
            return data.find('PRODUCTION: bool = True') != -1

    async def handle_digital_tune_all(self):
        await self.model.digital_tune_all()
        return True

    async def handle_get_ad_ul_dl(self) -> AdUlDl:
        return await self.model.get_ad_ul_dl()

    async def handle_set_gps_position(self, params: dict):
        coords = params['position']

        replaced_comma: str = coords.replace(',', '.')
        splitted_coords: list = replaced_comma.split(' ')

        if len(splitted_coords) > 2:
            splitted_coords.pop(2)

        words: str = ''
        if float(splitted_coords[0]) >= 0:
            words += 'N'
        else:
            words += 'S'

        if float(splitted_coords[1]) >= 0:
            words += 'E'
        else:
            words += 'W'

        splitted_coords.append(words)
        coords = ' '.join(splitted_coords)

        save_const('gps_position', coords)

        return True

    async def handle_set_current_time(self, params: dict):
        from common.third_party.save_config import save_config
        from services.server_integration.schemas.time_type import TimeType

        if not self.model.server_time_sync:
            current_time = params['current_time']
            if load_config('tm/gps_set_time_time', float, 0.0) == 0.0:
                current_time = datetime.datetime.fromtimestamp(current_time) + datetime.timedelta(seconds=4, hours=-self.model.full_unit_config.unit_configuration.timezone)
                subprocess.call(
                    ['date', '-s', current_time.strftime('%Y-%m-%d %H:%M:%S')],
                    stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
                )
                self.model.alarm_db.update_current_time_point()
                self.model.hands_time_sync = True
                save_config("block_time/time_type", str(TimeType.HANDS))

        return True

    def __init__(self, loop) -> None:
        self.model = ServerIntegration(loop)

        time.sleep(22)
        asyncio.create_task(self.model.process_full_unit_config(self.model.full_unit_config, True, self.model.full_unit_config))
        ad_api = Ad936xApi()
        asyncio.create_task(ad_api.digital_tune_all())

        super(ServerIntegrationHandler, self).__init__(
            request_queue_uuid=settings.SERVER_INTEGRATION_REQUEST_QUEUE
        )

        self.handle('set_full_configuration', self.handle_set_full_configuration)
        self.handle('get_full_configuration', self.handle_get_full_configuration)
        self.handle('start_calibrate', self.handle_start_calibrate)
        self.handle('special_calibrate', self.handle_special_calibrate)
        self.handle('set_ac_magic_numbers', self.handle_set_ac_magic_numbers)
        self.handle('get_ac_magic_numbers', self.handle_get_ac_magic_numbers)

        self.handle('get_technical_status', self.handle_get_technical_status)
        self.handle('get_newest_oldest_alarms_date', self.handle_get_newest_oldest_alarms_date)

        self.handle('set_rtr_normal_vswr', self.handle_set_rtr_normal_vswr)
        self.handle('get_rtr_normal_vswr', self.handle_get_rtr_normal_vswr)
        self.handle('calibrate_rtr_vswr', self.handle_calibrate_rtr_vswr)

        self.handle('clear_alarms', self.handle_clear_alarms)
        self.handle('get_cities_timezones', self.handle_get_cities_timezones)
        self.handle('get_city_by_timezone', self.handle_get_city_by_timezone)
        self.handle('get_current_alarms', self.handle_get_current_alarms)
        self.handle('get_alarms_log', self.handle_get_alarms_log)
        self.handle('raise_info', self.handle_raise_info)
        self.handle('raise_alarm', self.handle_raise_alarm)
        self.handle('load_alarms_descriptions', self.handle_load_alarms_descriptions)
        self.handle('reboot_motherboard', self.handle_reboot_motherboard)
        self.handle('reboot_power', self.handle_reboot_power)
        
        self.handle('get_server_ip', self.handle_get_server_ip)
        self.handle('get_server_proxy_ip', self.handle_get_server_proxy_ip)
        self.handle('get_serial_number', self.handle_get_serial_number)
        self.handle('get_cviya_list', self.handle_get_cviya_list)

        self.handle('set_calibrate_status', self.handle_set_calibrate_status)

        self.handle('get_timezone', self.handle_get_timezone)

        self.handle('zcalibration_calibrate', self.handle_zcalibration_calibrate)
        self.handle('zcalibration_telemetry', self.handle_zcalibration_telemetry)
        self.handle('zcalibration_info', self.handle_zcalibration_info)
        self.handle('zcalibration_save', self.handle_zcalibration_save)

        self.handle('set_production_state', self.handle_set_production_state)
        self.handle('get_production_state', self.handle_get_production_state)

        self.handle('digital_tune_all', self.handle_digital_tune_all)
        self.handle('get_ad_ul_dl', self.handle_get_ad_ul_dl)

        self.handle('set_gps_position', self.handle_set_gps_position)
        self.handle('set_current_time', self.handle_set_current_time)
